#include "httpParser.h"
#include <string>
#include "common.h"
#include "log4z.h"


const char* getFileType(const char* name)
{
	const char* dot = NULL;

	dot = strrchr(name, '.');
	if (dot == NULL)
		return "text/plain; charset=utf-8";
	if (strcmp(dot, "js") == 0)
		return "application/javascript";
	if (strcmp(dot, ".json") == 0)
		return "application/json;charset=utf-8";
	if (strcmp(dot, ".pdf") == 0)
		return "application/pdf";
	if (strcmp(dot, ".html") == 0 ||
		strcmp(dot, ".htm") == 0)
		return "text/html; charset=utf-8";
	if (strcmp(dot, "ioc") == 0)
		return "image/x-icon";
	if (strcmp(dot, "bmp") == 0)
		return "image/bitmap";
	if (strcmp(dot, ".jpg") == 0 ||
		strcmp(dot, ".jpeg") == 0)
		return "image/jpeg";
	if (strcmp(dot, ".gif") == 0)
		return "image/gif";
	if (strcmp(dot, ".png") == 0)
		return "image/png";
	if (strcmp(dot, ".css") == 0)
		return "text/css";
	if (strcmp(dot, ".au") == 0)
		return "audio/basic";
	if (strcmp(dot, ".wav") == 0)
		return "audio/wav";
	if (strcmp(dot, ".avi") == 0)
		return "video/x-msvideo";
	if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
		return "video/quicktime";
	if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
		return "video/mpeg";
	if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
		return "model/vrml";
	if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
		return "audio/midi";
	if (strcmp(dot, ".mp3") == 0)
		return "audio/mpeg";
	if (strcmp(dot, ".mp4") == 0)
		return "audio/mp4";
	if (strcmp(dot, ".ogg") == 0)
		return "application/ogg";
	if (strcmp(dot, ".pac") == 0)
		return "application/x-ns-proxy-autoconfig";
	if (strcmp(dot, ".pcap") == 0)
		return "application/x-pcap";

	return "text/plain; charset=utf-8";
}

size_t GetFileSize(const char* filename)
{
	struct stat st;
	if (stat(filename, &st) == -1) {
		return 0;
	}
	return st.st_size;
}

char* GetFileModificationTime(const char* filename, char* timeNow)
{
	struct stat st;
	if (stat(filename, &st) == -1) {
		return 0;
	}
	struct tm* ttime;
	ttime = localtime(&st.st_mtime);
	strftime(timeNow, 64, "%Y-%m-%d %H:%M:%S", ttime);

	//struct tm* t = localtime(&file_message->st_mtime);		//struct stat* file_message
	//printf("%2d月 %2d %02d:%02d ", t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min);

	return timeNow;
}

char* GetFileCreateTime(const char* filename, char* timeNow)
{
	struct stat st;
	if (stat(filename, &st) == -1) {
		return 0;
	}
	struct tm* ttime;
	ttime = localtime(&st.st_ctime);
	strftime(timeNow, 64, "%Y-%m-%d %H:%M:%S", ttime);

	//struct tm* t = localtime(&file_message->st_mtime);		//struct stat* file_message
	//printf("%2d月 %2d %02d:%02d ", t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min);

	return timeNow;
}

#ifdef WIN32

static void UTF_8ToUnicode(WCHAR* pOut, char* pText)
{
	char* uchar = (char*)pOut;

	uchar[1] = ((pText[0] & 0x0F) << 4) + ((pText[1] >> 2) & 0x0F);

	uchar[0] = ((pText[1] & 0x03) << 6) + (pText[2] & 0x3F);
}

static int urldecode(const char* encd, unsigned int len, char* url)
{
	unsigned int j = 0, i;
	char p[4] = { 0 };
	char decd[1024] = { 0 };
	char* cd = (char*)encd;

	for (i = 0; i < len; i++) {
		memset(p, '\0', 2);
		if (cd[i] != '%')
		{
			decd[j++] = cd[i];
			continue;
		}

		p[0] = cd[++i];
		p[1] = cd[++i];

		p[0] = p[0] - 48 - ((p[0] >= 'A') ? 7 : 0) - ((p[0] >= 'a') ? 32 : 0);
		p[1] = p[1] - 48 - ((p[1] >= 'A') ? 7 : 0) - ((p[1] >= 'a') ? 32 : 0);
		decd[j++] = char(p[0] * 16 + p[1]);
	}

	decd[j] = '\0';
	int u = 0, k = 0;
	char buf[4] = { 0 };

	{
		while (u < j) {
			if (*(decd + u) >= 0) {
				url[k++] = decd[u++];
			}
			else {
				WCHAR Wtemp;

				unsigned short int tmp = 0;

				UTF_8ToUnicode(&Wtemp, decd + u);

				WideCharToMultiByte(CP_ACP, NULL, &Wtemp, 1, buf, sizeof(WCHAR), NULL, NULL);

				tmp = url[k] = buf[0];
				tmp = url[k + 1] = buf[1];
				tmp = url[k + 2] = buf[2];

				u += 3;
				k += 2;
			}
		}
		url[k] = '\0';
	}
	return k;
}
#else
unsigned char Hex(unsigned char x)
{
	unsigned char y = 0;
	if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;
	else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;
	else if (x >= '0' && x <= '9') y = x - '0';
	return y;
}

static int urldecode(const char* enc, uint32_t len, char* url)
{
	uint32_t i = 0, j = 0;
	for (i = 0; i < len; i++)
	{
		if (enc[i] == '+') {
			url[j++] = ' ';
		}
		else if (enc[i] == '%') {
			uint8 high = Hex((unsigned char)enc[++i]);
			uint8 low = Hex((unsigned char)enc[++i]);
			url[j++] = uint8(high * 16 + low);
		}
		else url[j++] = enc[i];
	}

	url[j] = 0;

	return j;
}
#endif

int CHttpResponse::make(char* szBuffer, int nContentLen, const char* szContentType)
{
	char* ptr = szBuffer;

	ptr += sprintf(ptr, "HTTP/1.1 200 OK\r\n");
	//ptr += sprintf(ptr, "Access-Control-Allow-Credentials: true\r\n");
	//ptr += sprintf(ptr, "Access-Control-Allow-Origin: *\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Origin: *\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE\r\n");
	ptr += sprintf(ptr, "Access-Control-Max-Age: 3600\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Headers: authorization,x-requested-with,content-type\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Credentials: true\r\n");

	ptr += sprintf(ptr, "Connection: keep-alive\r\n");
	ptr += sprintf(ptr, "Content-Length: %d\r\n", nContentLen);
	ptr += sprintf(ptr, "Content-Type: %s\r\n\r\n", szContentType);

	return int(ptr - szBuffer);
}

int CHttpResponse::make(char* szBuffer, FILE* pFile, const char* sFileName, int nFilelen)
{
	char* ptr = szBuffer;

	ptr += sprintf(ptr, "HTTP/1.1 200 OK\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Credentials: true\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Origin: *\r\n");
	ptr += sprintf(ptr, "Connection: keep-alive\r\n");
	ptr += sprintf(ptr, "Content-Length: %d\r\n", nFilelen);
	ptr += sprintf(ptr, "Content-Type: %s\r\n\r\n", getFileType(sFileName));

	//  ptr += fread(ptr, 1, nFilelen, pFile);
	//  size_t bytesRead = 0;
	//  while ((bytesRead = fread(ptr, 1, 4096, pFile)) > 0) {
	// 	ptr += bytesRead;
	//  }

	return int(ptr - szBuffer);
}

int CHttpResponse::make_download(char* szBuffer, const char* sFileName, int nFilelen)
{
	char* ptr = szBuffer;

	ptr += sprintf(ptr, "HTTP/1.1 200 OK\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Credentials: true\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Origin: *\r\n");
	ptr += sprintf(ptr, "Access-Control-Allow-Methods: GET,POST,OPTIONS,PUT,DELETE\r\n");
	//  ptr += sprintf(ptr, "Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Thu, 23-Jun-2022 02:39:46 GMT\r\n");
	ptr += sprintf(ptr, "Connection: keep-alive\r\n");
	ptr += sprintf(ptr, "Vary: Origin\r\n");
	ptr += sprintf(ptr, "Vary: Access-Control-Request-Method\r\n");
	ptr += sprintf(ptr, "Vary: Access-Control-Request-Headers\r\n");
	ptr += sprintf(ptr, "Content-Length: %d\r\n", nFilelen);
	/* ptr += sprintf(ptr, "Transfer-Encoding: chunked\r\n");*/
	ptr += sprintf(ptr, "Content-Disposition: attachment;filename=%s\r\n", sFileName);
	ptr += sprintf(ptr, "Content-Type: application/force-download\r\n\r\n");


	return int(ptr - szBuffer);
}

void CHttpResponse::make_200(CData& strResp)
{
	strResp += "HTTP/1.1 200 OK\r\n";
	strResp += "Access-Control-Allow-Credentials: true\r\n";
	strResp += "Access-Control-Allow-Origin: *\r\n";
	strResp += "Connection: keep-alive\r\n";
	strResp += "Content-Length: 1\r\n";
	strResp += "Content-Type: application/json;charset=utf-8\r\n\r\n0";
}

void CHttpResponse::make_404_error(CData& strResp)
{
	CData strContent;
	strContent += "<html>\r\n";
	strContent += "<head><title>404 Not Found</title></head>\r\n";
	strContent += "<body bgcolor=\"white\">\r\n";
	strContent += "<center><h1>404 Not Found</h1></center>\r\n";
#if defined AQRH_WEBSERVER
	strContent += "<hr><center>安全超融合服务</center>\r\n";
#else
	strContent += "<hr><center>http_server</center>\r\n";
#endif
	strContent += "</body>\r\n";
	strContent += "</html>\r\n";
	strContent += "<!-- The padding to disable MSIE's friendly error page -->";
	strContent += "<!-- The padding to disable MSIE's friendly error page -->";
	strContent += "<!-- The padding to disable MSIE's friendly error page -->";
	strContent += "<!-- The padding to disable MSIE's friendly error page -->";
	strContent += "<!-- The padding to disable MSIE's friendly error page -->";
	strContent += "<!-- The padding to disable MSIE's friendly error page -->";

	char szTemp[100];
	sprintf(szTemp, "Content-Length: %d\r\n", (int)strContent.size());

	strResp = "HTTP/1.1 404 Not Found\r\n";
	strResp += "Server: http_util\r\n";
	strResp += "Content-Type: text/html; charset=UTF-8\r\n";
	strResp += szTemp;
	strResp += "Connection: keep-alive\r\n";
	strResp += "\r\n";
	strResp += strContent;
}
void CHttpResponse::make_302_error(const CData& strLocation, const CData& strMoveTo, CData& strResp)
{
	CData strContent;
	strContent += "<html><head><title>Object moved</title></head><body>\r\n";
	strContent += "<h2>Object moved to <a href=\"";
	strContent += strMoveTo;
	strContent += "\">here</a>.</h2>\r\n";
	strContent += "</body></html>\r\n";

	char szTemp[100];
	sprintf(szTemp, "Content-Length: %d\r\n", (int)strContent.size());

	strResp = "HTTP/1.1 302 Found\r\n";
	strResp += "Server: http_util\r\n";
	strResp += "Content-Type: text/html; charset=UTF-8\r\n";
	strResp += szTemp;
	strResp += "Connection: keep-alive\r\n";
	strResp += "Location: ";
	strResp += strLocation + "\r\n";
	strResp += "\r\n";
	strResp += strContent;
}

CHttpParser::CHttpParser()
{
	m_method = HTTP_METHOD_NONE;
}

CHttpParser::~CHttpParser()
{
	Clear();
}

static int  GetHttpline(const char* data, int len, CData& obj)
{
	int i = 0;
	while (i + 1 < len)
	{
		if (data[i] == '\r' && data[i + 1] == '\n') {
			obj.setData(data, i);
			return XX_ERR_NONE;
		}
		i++;
	}
	return XX_ERR_ERROR;
}

void CHttpParser::SetContent(const char* key, const char* content, int len)
{

}

int CHttpParser::parse(const char* data, int ulen)
{
	CData lineData(2048, true);
	int len = 0, line = 0;
	Clear();

	int count = NUM_MAX;
	while (count--)
	{
		if (GetHttpline(data + len, ulen - len, lineData)) {
			len = XX_ERR_NONE;
			break;
		}

		len += (lineData.size() + 2);
		if (lineData.empty()) {
			int nContent = GetContentLength();
			m_type = GetContentType();
			std::string::size_type idx;
			idx = m_type.find("form-data");
			if (nContent > 0) {
				if ((len + nContent) <= ulen) {
					SetContent(m_headers.c_str(), data + len, nContent);
					m_Context.setData(data + len, nContent);
					len += nContent;
				}
				else {
					len = XX_ERR_NONE;
				}
			}
			break;
		}

		if (ParseLine(lineData, ++line)) {
			len = XX_ERR_ERROR;
			break;
		}
	}

	LOGFMTI("count:%d", count);
	return len;
}

int CHttpParser::parsePostParam() {
	CData content = m_Context;
	CData param1, pLeft;
	pLeft = content;
	int count = NUM_MAX;
	while (count--) {
		int nRet = pLeft.substr(param1, pLeft, "&");

		if (0 != nRet)
		{
			CData key, value;
			pLeft.substr(key, value, "=");
			key.ToLower();
			m_ParamMap[key] = value;
			break;
		}
		else
		{
			CData key, value;
			param1.substr(key, value, "=");
			key.ToLower();
			m_ParamMap[key] = value;
		}
	}
	LOGFMTI("count:%d", count);
	return XX_ERR_NONE;
}

int CHttpParser::parseGetParam() {
	CData getParam = get_object();
	getParam.clean("?", false);
	CData param1, pLeft;
	pLeft = getParam;
	int count = NUM_MAX;
	while (count--) {
		int nRet = pLeft.substr(param1, pLeft, "&");
		if (0 != nRet)
		{
			CData key, value;
			pLeft.substr(key, value, "=");
			key.ToLower();
			m_ParamMap[key] = value;
			break;
		}
		else
		{
			CData key, value;
			param1.substr(key, value, "=");
			key.ToLower();
			m_ParamMap[key] = value;
		}
	}
	LOGFMTI("count:%d", count);
	return XX_ERR_NONE;
}

int CHttpParser::ParseLine(CData& lineData, int lineNum)
{
	if (lineNum == 1) {
		if (lineData.Compare("GET") == 0) {
			m_method = HTTP_METHOD_GET;
		}
		else if (lineData.Compare("POST") == 0) {
			m_method = HTTP_METHOD_POST;
		}
		else if (lineData.Compare("OPTIONS") == 0) {
			m_method = HTTP_METHOD_OPTIONS;
		}
		else if (lineData.Compare("HTTP/") == 0) {
			m_method = HTTP_METHOD_RESP;
		}
		else {
			return XX_ERR_ERROR;
		}

		lineData.substr(" ", " HTTP", m_headers);
	}
	else if (lineNum > 1)  // header name/value
	{
		CData fieldName, fieldValue;

		if (lineData.substr(fieldName, fieldValue, ": ")) {
			return XX_ERR_ERROR;
		}
		fieldName.ToLower();
		m_FieldMap[fieldName] = fieldValue;
	}

	return 0;
}

int CHttpParser::Clear()
{
	m_FieldMap.clear();
	m_ParamMap.clear();
	m_headers.Remove();
	m_Context.Remove();
	return XX_ERR_NONE;
}

int CHttpParser::GetMethod()
{
	return m_method;
}

int CHttpParser::GetContentLength()
{
	const char* tContent = get_tpfield("Content-Length");
	if (NULL == tContent) {
		tContent = get_tpfield("Content-length");
	}

	if (NULL != tContent) {
		return atoi(tContent);
	}

	return 0;
}

const char* CHttpParser::GetContentType()
{
	const char* tContent = get_tpfield("Content-Type");
	if (NULL == tContent) {
		tContent = get_tpfield("Content-Type");
	}

	if (NULL != tContent) {
		return tContent;
	}

	return "";
}

const char* CHttpParser::GetAuthorization()
{
	const char* tContent = get_tpfield("Authorization");
	if (NULL == tContent) {
		tContent = get_tpfield("Authorization");
	}

	if (NULL != tContent) {
		return tContent;
	}

	return "";
}

const char* CHttpParser::get_uri()
{
	return m_headers.c_str();
}

const char* CHttpParser::get_object()
{
	return GetUrlPath(m_headers.c_str(), "/");
}

CData CHttpParser::getHeader() {
	return m_headers;
}

const char* CHttpParser::get_tpfield(const char* szKeys)
{
	CData keys(szKeys);
	keys.ToLower();
	std::map<CData, CData>::iterator Iter;
	Iter = m_FieldMap.find(keys);
	if (Iter != m_FieldMap.end()) {
		return Iter->second.c_str();
	}
	return NULL;
}

const char* CHttpParser::get_paramField(const char* szKeys) {
	CData keys(szKeys);
	keys.ToLower();
	std::map<CData, CData>::iterator Iter;
	Iter = m_ParamMap.find(keys);
	if (Iter != m_ParamMap.end()) {
		return Iter->second.c_str();
	}
	return "";
}
